module mem(
    input [63:0] mem_data_i,
    input [63:0] mem_wdata_i,
    input [3:0] mem_control,
    input [63:0] mem_addr,
    input [63:0] i_badvaddr,
    input [63:0] i_excode,
    input i_except_ena,

    output reg [63:0] mem_data_o,
    output reg [7:0] mem_mask,
    output [63:0] o_badvaddr,
    output [63:0] o_excode,
    output o_except_ena,
    output [1:0] control,
);

    always @(mem_control) begin
        case (mem_control)
            4'b0000 : begin
                mem_data_o = {{56{mem_data_i[7]}}, mem_data_i[7:0]};
            end
            4'b0001 : begin
                mem_data_o = {{48{mem_data_i[15]}}, mem_data_i[15:0]};
            end
            4'b0010 : begin
                mem_data_o = {56'd0, mem_data_i[7:0]};
            end
            4'b0011 : begin
                mem_data_o = {48'd0, mem_data_i[15:0]};
            end
            4'b0100 : begin
                mem_data_o = {{32{mem_data_i[31]}}, mem_data_i[31:0]};
            end
            4'b0101 : begin
                mem_data_o = {32'd0, mem_data_i[31:0]};
            end
            4'b0110 : begin
                mem_data_o = mem_data_i;
            end
            4'b0111 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00000001;
                    end
                    3'b001 : begin
                        mem_mask = 8'b00000010;
                    end
                    3'b010 : begin
                        mem_mask = 8'b00000100;
                    end
                    3'b011 : begin
                        mem_mask = 8'b00001000;
                    end
                    3'b100 : begin
                        mem_mask = 8'b00010000;
                    end
                    3'b101 : begin
                        mem_mask = 8'b00100000;
                    end
                    3'b110 : begin
                        mem_mask = 8'b01000000;
                    end
                    3'b111 : begin
                        mem_mask = 8'b10000000;
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1000 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00000011;
                    end
                    3'b010 : begin
                        mem_mask = 8'b00001100;
                    end
                    3'b100 : begin
                        mem_mask = 8'b00110000;
                    end
                    3'b110 : begin
                        mem_mask = 8'b11000000;
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1001 : begin
                case(mem_addr[2:0])
                    3'b000 : begin
                        mem_mask = 8'b00001111;
                    end
                    3'b100 : begin
                        mem_mask = 8'b11110000;
                    end
                    default : begin
                        mem_mask = 8'b00000000;
                    end
                endcase
            end
            4'b1010 : begin
                mem_mask = 8'b11111111;
            end
            default : begin
                mem_mask = 8'b00000000;
                mem_data_o = 64'd0;
            end
        endcase
    end    


    assign control = (mem_control == 4'b0000 | mem_control == 4'b0001 | mem_control == 4'b0010 | mem_control == 4'b0011 | mem_control == 4'b0100 | mem_control == 4'b0101 | mem_control == 4'b0110) ? 2'b01 : (mem_control == 4'b0111 | mem_control == 4'b1000 | mem_control == 4'b1001 | mem_control == 4'b1010 ? 2'b10 : 2'b00); 
    assign o_except_ena = i_except_ena ? 1'b1 : (((mem_control == 4'b0001 || mem_control == 4'b0011) && mem_addr[0] != 1'b0) || ((mem_control == 4'b0100 || mem_control == 4'b0101) && mem_addr[1:0] == 2'b00) || (mem_control == 4'b0110 && mem_addr[2:0] == 3'b000) || ((mem_control == 4'b1000 && mem_addr[0] == 1'b0) || (mem_control == 4'b1001 && mem_addr[1:0] == 2'b00) || (mem_control == 4'b1010 && mem_addr[2:0] == 3'b000)));
    assign o_excode = i_except_ena ? i_excode : (((mem_control == 4'b0001 || mem_control == 4'b0011) && mem_addr[0] != 1'b0) || ((mem_control == 4'b0100 || mem_control == 4'b0101) && mem_addr[1:0] == 2'b00) || (mem_control == 4'b0110 && mem_addr[2:0] == 3'b000) ? {1'b0, 63'd4} : ((mem_control == 4'b1000 && mem_addr[0] == 1'b0) || (mem_control == 4'b1001 && mem_addr[1:0] == 2'b00) || (mem_control == 4'b1010 && mem_addr[2:0] == 3'b000) ? {1'b0, 63'd6} : 64'd0));
endmodule
